package pub.gll.plugin.asm.annotation;


import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.NEW;


import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import pub.gll.plugin.util.CheckClass;
import pub.gll.plugin.util.CloseUtil;
import pub.gll.plugin.util.Log;

public class ArchParseClassAdapter extends ClassVisitor {

    private boolean hasParseMethod = false;
    private String parseMethodName = "parse";
    private String parseMethodDesc = "()V";
    private String className;
    private String superClassName;

    public ArchParseClassAdapter(int api, ClassVisitor cv) {
        super(api, cv);
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
        superClassName = superName;
        className = name;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        if (parseMethodName.equals(name) && parseMethodDesc.equals(desc)) {
            hasParseMethod = true;
            if (needParse()) {
                throw new RuntimeException("不能复写arch类的parse方法");
            }
        }
        return super.visitMethod(access, name, desc, signature, exceptions);
    }

    private boolean needParse() {
        return CollectArchBindingDataContainer.containsName(className) && CheckClass.isSubClassWithRulesAndIsNotSelf(className);
    }

    @Override
    public void visitEnd() {
        if (!hasParseMethod && needParse()) {
            Log.d("CollectArchBindingDataContainer", "insert class: " + className);
            String archBindingClassName = className + CollectArchBindingDataContainer.ARCH_END_POSIX;
            MethodVisitor mv = super.visitMethod(Opcodes.ACC_PUBLIC, "parse", "()V", null, null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, superClassName, "parse", "()V");
            mv.visitTypeInsn(NEW, archBindingClassName);
            mv.visitInsn(DUP);
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, archBindingClassName, "<init>", "(" + "L" + className + ";" + ")V");
            mv.visitVarInsn(ASTORE, 1);
            mv.visitInsn(Opcodes.RETURN);
            mv.visitMaxs(2, 2);
            mv.visitEnd();
            CollectArchBindingDataContainer.increaseRealInsertCount();
        } else {
            if (CollectArchBindingDataContainer.containsName(className)) {
                CloseUtil.exit("没有插入：hasParseMethod: " + hasParseMethod + ", needParse is : " + needParse());
            }
        }
        super.visitEnd();
    }
}
